package othello.core;

import java.util.List;
import java.util.ArrayList;

public class Board{
    //石の色白を表す定数
    public static final int WHITE  = -1;
    //石の色黒を表す定数
    public static final int BLACK  =  1;
    //石がないことを表す定数
    private static final int EMPTY  =  0;
    //番兵を表す定数
    private static final int SENTRY =  9;

    //オセロ盤の最初の状態を表す配列
    private static final int[][] DEFAULT_BOARD = {
        { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9},
        { 9, 0, 0, 0, 0, 0, 0, 0, 0, 9},
        { 9, 0, 0, 0, 0, 0, 0, 0, 0, 9},
        { 9, 0, 0, 0, 0, 0, 0, 0, 0, 9},
        { 9, 0, 0, 0,-1, 1, 0, 0, 0, 9},
        { 9, 0, 0, 0, 1,-1, 0, 0, 0, 9},
        { 9, 0, 0, 0, 0, 0, 0, 0, 0, 9},
        { 9, 0, 0, 0, 0, 0, 0, 0, 0, 9},
        { 9, 0, 0, 0, 0, 0, 0, 0, 0, 9},
        { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}
    };

    //オセロ盤を表す配列
    private int[][] boardData;
    //オセロ盤に置いてある石の数
    private int stoneCount;

    //オセロ盤を最初の状態で初期化し、石の数を4とする
    public Board(){
        boardData = Board.cloneBoardData(DEFAULT_BOARD);
        stoneCount = 4;
    }

    //色colorの石を座標(x,y)に置く
    public void putStone(int color, int x, int y) throws BadHandException{
        //座標がオセロ盤の外ならBadHandExceptionを発生させる
        if(x < 1 || 8 < x)
            throw new BadHandException("BadHand", x, y);

        if(y < 1 || 8 < y)
            throw new BadHandException("BadHand", x, y);

        //ひっくり返せる全ての石の位置を取得
        List<CellPoint> reverseList =
            this.getReverseList(color, new CellPoint(x, y));

        //ひっくり返せる石がなければBadHandExceptionを発生させる
        if(reverseList.size() == 0)
            throw new BadHandException("BadHand", x, y);

        //ひっくり返せる位置を全てcolorに変える
        for(CellPoint point : reverseList)
            this.setCell(color, point.getX(), point.getY());

        setCell(color, x, y);
    }

    private static final int X = 0;
    private static final int Y = 1;

    private static final int[][] VOLUTE = {
        {-1, -1}, {-1,  0}, {-1,  1}, { 0,  1},
        { 1,  1}, { 1,  0}, { 1, -1}, { 0, -1}
    };

    //色colorで座標CellPointに置くことでひっくり返せるすべての座標を返す
    private List<CellPoint> getReverseList(int color, CellPoint current){
        List<CellPoint> reverseList = new ArrayList<CellPoint>();

        //この座標が空いていなければサイズ0のリストを返す
        if(!this.isEmptyCell(current))
            return reverseList;

        //colorとは逆の色
        int reverseColor = -color;

        //全方向にひっくり返せる石がないかチェックする
        for(int[] direction : VOLUTE){
            int dx = current.getX();
            int dy = current.getY();
            int reverseCount = 0;

            //ひっくり返せるかもしれない候補を入れるリスト
            List<CellPoint> tempReverseList = new ArrayList<CellPoint>();

            while(true){
                //隣に移動
                dx += direction[X];
                dy += direction[Y];

                //隣が逆の色でなければループ終了
                if(this.getCell(dx, dy) != reverseColor)
                    break;

                //一時的なリストに追加する
                tempReverseList.add(new CellPoint(dx, dy));
            }

            //現在位置がcolorで一時リストのサイズが0より大きければひっくり返せるリストに追加
            if(this.getCell(dx, dy) == color && tempReverseList.size() > 0)
                reverseList.addAll(tempReverseList);
        }

        return reverseList;
    }

    //座標(x,y)をcolorにする
    public void setCell(int color, int x, int y){
        if(isEmptyCell(x, y))
            stoneCount++;

        boardData[y][x] = color;
    }

    public int getCell(CellPoint point){
        return this.getCell(point.getX(), point.getY());
    }

    public int getCell(int x, int y){
        return boardData[y][x];
    }

    public int getStoneCount(){
        return stoneCount;
    }

    //色colorの石の数を数える
    public int getStoneCount(int color){
        int stoneCount = 0;

        for(int y = 1; y <= 9; y++){
            for(int x = 1; x <= 9; x++){
                if(getCell(x, y) == color)
                    stoneCount++;
            }
        }

        return stoneCount;
    }

    public boolean isWhiteCell(CellPoint point){
        return this.isWhiteCell(point.getX(), point.getY());
    }

    public boolean isWhiteCell(int x, int y){
        return this.getCell(x, y) == Board.WHITE;
    }

    public boolean isBlackCell(CellPoint point){
        return this.isBlackCell(point.getX(), point.getY());
    }

    public boolean isBlackCell(int x, int y){
        return this.getCell(x, y) == Board.BLACK;
    }

    public boolean isEmptyCell(CellPoint point){
        return this.isEmptyCell(point.getX(), point.getY());
    }

    public boolean isEmptyCell(int x, int y){
        return this.getCell(x, y) == Board.EMPTY;
    }

    private int[][] getBoardData(){
        return boardData;
    }

    private void setBoardData(int[][] boardData){
        this.boardData = boardData;
    }

    //このボードと同じ構造のボードを返す
    @Override
    public Object clone(){
        Board cloneBoard = new Board();
        cloneBoard.setBoardData(Board.cloneBoardData(this.getBoardData()));

        return cloneBoard;
    }

    //srcの配列の配列と同じ構造の配列の配列を作る
    private static int[][] cloneBoardData(int[][] src){
        int[][] clone = new int[src.length][];

        for(int y = 0; y < src.length; y++){
            clone[y] = new int[src[y].length];

            for(int x = 0; x < src[y].length; x++){
                clone[y][x] = src[y][x];
            }
        }

        return clone;
    }
}